#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cbm.h>
#include <conio.h>

#include "Buffers.h"
#include "Trim.h"
#include "Splitter.h"
#include "Drives.h"
#include "Dispatcher.h"

#pragma code-name("MKIMGCODE");
#pragma rodata-name("MKIMGCODE");

#define D64_SIZE  689
#define D71_SIZE 1377
#define D81_SIZE 3226

enum
{
	F_UNKNOWN,
	F_1541,						// single-sided
	F_1571,						// double-sided
	F_1581,
	F_2030,
	F_2040,
	F_8050,						// single-sided
	F_8250,						// double-sided
	F_D2M,
	F_DNP,
	F_FAT,
	F_IDE64
};

static const unsigned char l[] =	// sectors per track on 1541/1571 disks
// Note:  This table can handle both sides of a 1571 because the modulo
// operator effectively "unfolds" it into two copies.
{	21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
	19,19,19,19,19,19,19,
	18,18,18,18,18,18,
	17,17,17,17,17
};

// Detect the format of the disk in the named drive.
//
static unsigned char __fastcall__ getFormat(unsigned char drive)
{
	int r;
	unsigned char format = F_UNKNOWN;

	// Decode the drive's reset-message.
	memset(buffer1, 0, sizeof(buffer1));
	cbm_open(15, drive, 15, "ui");
	if ((r = cbm_read(15, buffer1, sizeof(buffer1))) > 0)
	{
		buffer1[r] = '\0';
		if (strstr(buffer1,"1541") != NULL ||
			strstr(buffer1,"1540") != NULL ||
			strstr(buffer1,"1570") != NULL)
		{
			format = F_1541;
		}
		else if(strstr(buffer1,"1581") != NULL)
		{
			format = F_1581;
		}
		else if(strstr(buffer1,"ide64") != NULL)
		{
			format = F_IDE64;
		}
		else
		{
			// Either the message doesn't tell the format, or it's ambiguous.
			// Look at the format-codes at the beginning of the raw directory
			// file.
			// (Actually, this method could be used instead of the
			// reset-message, for almost all formats.)
			cbm_open(2, drive, 2, "$");
			if ((r = cbm_read(2, buffer1, 2)) > 0)
			{
				buffer1[r] = '\0';
				switch (buffer1[0])
				{
				case 'a':
					// See if it's double- or single-sided.
					format = ((signed char)buffer1[1] < 0) ? F_1571 : F_1541;
					break;
				case 'd':
					format = F_1581;
					break;
				case 'h':
					format = F_DNP;
					break;
//#ifdef __C64__
//				case 'i':
//					format = F_IDE64;
//					break;
//#endif
				}
			}
			cbm_close(2);
		}
	}
	cbm_close(15);

	if (r <= 0)
	{
		printf("\nError getting format [%d]", r);
	}

	return format;
}

unsigned char makeImage(unsigned char driveIndex)
{
	int r = 0, x = 0, y = wherey() + 1;
	unsigned char result = 0;
	drive_t *drive = &drives[driveIndex];
	tokenCollection_t *collection = NULL;
	parsedPath_t *spath = NULL;
	parsedPath_t *tpath = NULL;
	static char buffer[256];
	char *text = NULL;
	unsigned char sd, td, i, j, sectorsThisTrack, tracks;
	bool isD64 = true, isD71 = false;
	unsigned int p = 0, size = D64_SIZE;

	if(0) cgetc();

	strcpy(buffer, buffer3);
	collection = splitString(buffer);
	text = collection->tokens[1].text;

	if(collection->count != 3)
	{
		printf("\nusage: mkimg source target\n");
		free(collection);
		return 0;
	}

	spath = parsePath(1, collection, driveIndex);

	driveIndex = getDrive(spath->device);
	if(driveIndex == 0xFF) 
	{
		printf("\nUnknown source device.");
		free(collection);
		free(spath);
		return 1;
	}
	drive = &drives[driveIndex];

	if(drive->type != CBM15x1)
	{
		printf("\nSource can only be 15x1 type drive.");
		free(collection);
		free(spath);
		return 4;
	}

	tpath = parsePath(2, collection, driveIndex);

	if(getDrive(tpath->device) == 0xFF) 
	{
		printf("\nUnknown target device.");
		free(collection);
		free(spath);
		free(tpath);
		return 2;
	}

	if(strlen(spath->file) > 0)
	{
		printf("\nSource must specify a drive only.");
		free(collection);
		free(spath);
		free(tpath);
		return 3;
	}

	if(strlen(tpath->file) == 0)
	{
		printf("\nTarget must specify a file.");
		free(collection);
		free(spath);
		free(tpath);
		return 5;
	}


	//if(selectedPanel != NULL && selectedPanel->drive != NULL)
	{
		if(
			//targetPanel->drive != NULL &&
			(sd = spath->device) !=
			(td = tpath->device))
		{
			switch (j = getFormat(sd))
			{
			case F_1541:
//				printf("\n%s","Making D64");
				//waitForEnterEsc();
				printf("\nMaking D64.");
				break;
			case F_1571:
				isD64 = false;
				isD71 = true;
				size=D71_SIZE;
//				printf("\n%s","Making D71");
				//waitForEnterEsc();
				printf("\nMaking D71.");
				break;
			case F_1581:
				isD64 = false;
				//isD71 = false;
				size=D81_SIZE;
//				printf("\n%s","Making D81");
				//waitForEnterEsc();
				printf("\nMaking D81.");
				break;
			default:
				printf("\nUnsupported format: %u", j);
				//waitForEnterEsc();
				return 1;
			}

			x = 0; y = wherey() + 1;
			if(y > 24)
			{
				scrolldown();
				scrolldown();
				y = 24;
				x = 0;
			}
			//strlower(tpath->file);
			//if(strstr(tpath->file,".d64") == NULL &&
			//	strstr(tpath->file,".d41") == NULL)
			//{
			//	strcat(tpath->file, ".d64");
			//}

			if(drives[getDrive(tpath->device)].type != CBM15x1)
			{
				memset(buffer1, 0, sizeof(buffer1));
				sprintf(buffer1, "cd%s", tpath->path);
				if((r = cbm_open(15, tpath->device, 15, buffer1)) != 0)
				{
					printf("\nInvalid target path.");
					cbm_close(15);
					free(collection);
					free(spath);
					free(tpath);
					return 1;
				}
				cbm_close(15);
			}

			cbm_open(15, sd, 15, "");
			if((r = cbm_open(2, sd, 2, "#")) == 0)
			{
				//cbm_open(14,td,15,"");
				memset(buffer3, 0, sizeof(buffer3));
				sprintf(buffer3, "%s,p,w", tpath->file);
				if((signed char)(r = cbm_open(3,td,3,buffer3)) == 0)
				{
					tracks = isD64 ? 35 : (isD71 ? 35*2 : 80);
					for(i=0;i<tracks;++i)
					{
						sectorsThisTrack = (isD64 || isD71) ? l[i%35] : 40;
						for(j=0;j<sectorsThisTrack;++j)
						{
							if(kbhit() && cgetc() == CH_STOP)
							{
								// Break out of the outer loop by presetting
								// the track number beyond the highest
								// supported value.
								i=77*2;
								break;
							}

							memset(buffer2, 0, sizeof(buffer2));
							cbm_write(15, buffer2,
								sprintf(buffer2,"u1 2 0 %u %u", i+1, j));

							memset(buffer3, 0, sizeof(buffer3));
							cbm_read(15, buffer3, sizeof(buffer3));
							if(buffer3[0] != '0' || buffer3[1] != '0')
							{
								printf("\nError setting track and sector.");
								printf("\n%s", buffer3);
								cbm_close(2); cbm_close(3);
								cbm_close(15); cbm_close(14);
								free(collection);
								free(spath);
								free(tpath);
								return 8;
							}

							memset(buffer3, 0, 0x100);
							if((r = cbm_read(2,buffer3, 0x100)) == 0x100)
							{
								if((r = cbm_write(3, buffer3, 0x100)) == 0x100)
								{
									memset(buffer2, 0, sizeof(buffer2));
									sprintf(buffer2, "%s - t:%02d,  s:%02d", tpath->file, i + 1, j);
									cputsxy(x, y, buffer2);
								}
								else
								{
									printf("\nError writing buffer to target. [%02X]", r);
									cbm_close(2); cbm_close(3);
									cbm_close(15); cbm_close(14);
									free(collection);
									free(spath);
									free(tpath);
									return 7;
								}
							}
							else
							{
								printf("\nError reading buffer from source. [%02X]", r);
								cbm_close(2); cbm_close(3);
								cbm_close(15); cbm_close(14);
								free(collection);
								free(spath);
								free(tpath);
								return 6;
							}
						}
					}
					cbm_close(2); cbm_close(3);
					cbm_close(15); cbm_close(14);
					r = 0;
					printf("\nFinished creating %s", tpath->file);
				}
				else
				{
					printf("\n%s",r >= 0 ? "Target not there" : buffer);
					cbm_close(3); cbm_close(14);
				}
			}
			else
			{
				printf("\nSource: %s",
					r >= 0 ? "unit not there" : buffer);
			}
		}
	}

	cbm_close(2); cbm_close(3);
	cbm_close(15); cbm_close(14);
	free(collection);
	free(spath);
	free(tpath);
	return r;
}
